package com.locus.locus.server.dados;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.locus.locus.client.dados.TweetWrapper;

public final class DAO {
	private static final class DAOIterator<T> implements Iterator<T> {

		final private Iterator<T> iterator;
		final private PersistenceManager pm;

		public DAOIterator(Iterator<T> iterator, PersistenceManager pm) {
			this.iterator = iterator;
			this.pm = pm;
		}

		@Override
		public boolean hasNext() {
			final boolean hasNext = this.iterator.hasNext();
			if (!hasNext) {
				pm.close();
			}
			return hasNext;
		}

		@Override
		public T next() {
			return this.iterator.next();
		}

		@Override
		public void remove() {
			this.iterator.remove();
		}
	}

	private static final PersistenceManagerFactory PMF_SINGLETON = JDOHelper.getPersistenceManagerFactory("transactions-optional");
	private static final ConfiguracaoConjuntoBoxes CONFIGURACAO_BOXES;

	static {
		final PersistenceManager pm = DAO.PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + ConfiguracaoConjuntoBoxes.class.getName();
		final List<ConfiguracaoConjuntoBoxes> listaConfiguracao = new ArrayList<ConfiguracaoConjuntoBoxes>((List<ConfiguracaoConjuntoBoxes>) pm.newQuery(query).execute());
		
		if (listaConfiguracao.size() == 0) {
			CONFIGURACAO_BOXES = new ConfiguracaoConjuntoBoxes();

			pm.makePersistent(CONFIGURACAO_BOXES);
		} else {
			CONFIGURACAO_BOXES = listaConfiguracao.get(0);
			CONFIGURACAO_BOXES.deserializarRelacionamentos(pm);
		}
		pm.close();
	}

	public static synchronized ConfiguracaoConjuntoBoxes getConfiguracaoBoxes() {
		return CONFIGURACAO_BOXES;
	}

	public static <T> T buscar(Class<T> classe, String where) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName() + " where "
				+ where;
		final List<T> lista = (List<T>) pm.newQuery(query).execute();
		final Iterator<T> itLista = lista.iterator();
		final T resposta;

		if (itLista.hasNext()) {
			resposta = itLista.next();
		} else {
			resposta = null;
		}
		pm.close();

		return resposta;
	}
	
	public static <T extends Wrappable<? extends IsSerializable>> T buscarTransiente(Class<T> classe, String where) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName() + " where "
				+ where;
		final List<T> lista = (List<T>) pm.newQuery(query).execute();
		final Iterator<T> itLista = lista.iterator();
		final T resposta;

		if (itLista.hasNext()) {
			resposta = itLista.next();
			resposta.deserializarRelacionamentos(pm);
		} else {
			resposta = null;
		}
		pm.close();

		return resposta;
	}


	public static <T> List<T> buscar(Class<T> classe, String where, Object param) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName() + " where "
				+ where;
		final List<T> lista = (List<T>) pm.newQuery(query).execute(param);
		final ArrayList<T> resposta = new ArrayList<T>(lista);
		pm.close();

		return resposta;
	}

	public static <T> List<T> buscar(Class<T> classe) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName();
		final ArrayList<T> itens = new ArrayList<T>((List<T>) pm.newQuery(query).execute());

		pm.close();

		return itens;
	}

	public static <T extends Wrappable<? extends Serializable>> List<T> buscarTodosTransiente(
			Class<T> classe) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName();
		final List<T> itens = new ArrayList<T>((List<T>) pm.newQuery(query).execute());

		for (T t : itens) {
			t.deserializarRelacionamentos(pm);
		}

		pm.close();

		return itens;
	}

	public static <T extends IsSerializable, E extends Wrappable<T>> List<T> buscarTransiente(
			Class<E> classe) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName();
		final List<E> itens = new ArrayList<E>((List<E>) pm.newQuery(query).execute());
		final ArrayList<T> listaFinal = new ArrayList<T>();

		for (Wrappable<T> w : itens) {
			w.deserializarRelacionamentos(pm);
			listaFinal.add(w.gerarWrapper());
		}

		pm.close();

		return listaFinal;
	}

	public static <T> List<T> buscar(Class<T> classe, List<Key> itens) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		Query query = pm.newQuery(classe, ":p.contains(key)");
		final List<T> lista = (List<T>) query.execute(itens);

		pm.close();

		return lista;
	}

	public static <T extends IsSerializable> List<T> buscarTodos(
			Class<? extends Wrappable<T>> classe) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName();
		final List<Wrappable<T>> lista = (List<Wrappable<T>>) pm.newQuery(query).execute();
		final Iterator<Wrappable<T>> itList = lista.iterator();
		Wrappable<T> objeto;

		final ArrayList<T> listaFinal = new ArrayList<T>();

		while (itList.hasNext()) {
			objeto = itList.next();
			objeto.deserializarRelacionamentos(pm);
			listaFinal.add(objeto.gerarWrapper());
		}

		pm.close();

		return listaFinal;
	}

	public static <T extends IsSerializable> List<T> buscarTodos(
			Class<? extends Wrappable<T>> classe, String where) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName() + " where " + where;
		final List<Wrappable<T>> lista = (List<Wrappable<T>>) pm.newQuery(query).execute();
		final Iterator<Wrappable<T>> itLista = lista.iterator();
		Wrappable<T> objeto;

		ArrayList<T> listaFinal = new ArrayList<T>();

		while (itLista.hasNext()) {
			objeto = itLista.next();
			objeto.deserializarRelacionamentos(pm);
			listaFinal.add(objeto.gerarWrapper());
		}

		pm.close();

		return listaFinal;
	}
	
	public static List<TweetWrapper> buscarTweets(String id) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final Key key = KeyFactory.stringToKey(id);
		final List<Tweet> tweets = (List<Tweet>) pm.newQuery(Tweet.class, "chaveConfiguracaoEstabelecimento == :p").execute(key);
		final List<TweetWrapper> wrappers = new ArrayList<TweetWrapper>(tweets.size());
		
		for(Tweet t:tweets){
			wrappers.add(t.gerarWrapper());
		}
		
		pm.close();
		
		return wrappers;
	}

	public static <T extends IsSerializable> List<T> buscarTodos(Class<? extends Wrappable<T>> classe, String where, Object param) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select from " + classe.getName() + " where " + where;
		final List<Wrappable<T>> lista = (List<Wrappable<T>>) pm.newQuery(query).execute(param);
		final Iterator<Wrappable<T>> itLista = lista.iterator();
		Wrappable<T> objeto;

		ArrayList<T> listaFinal = new ArrayList<T>();

		while (itLista.hasNext()) {
			objeto = itLista.next();
			objeto.deserializarRelacionamentos(pm);
			listaFinal.add(objeto.gerarWrapper());
		}

		pm.close();

		return listaFinal;
	}

	public static <T> void salvar(T objeto) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		pm.makePersistent(objeto);
		pm.close();
	}

	public static <T> void salvarTodos(List<T> lista) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		pm.makePersistentAll(lista);

		pm.close();
	}
	
	public static <T extends Wrappable<? extends IsSerializable>> T buscarPorId(Class<T> classe, Key chave) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();

		final T item = pm.getObjectById(classe, chave);
		item.deserializarRelacionamentos(pm);

		pm.close();
		
		return item;
	}

	public static <T> void excluir(Class<T> classe, String chave) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();

		final T item = pm.getObjectById(classe, chave);
		if (item != null) {
			pm.deletePersistent(item);
		}

		pm.close();
	}

	public static <T> void excluirWhere(Class<T> classe, String where) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();

		pm.deletePersistentAll((Collection<T>)pm.newQuery("select from " + classe.getName() + " where " + where).execute());

		pm.close();
	}

	public static <T> Iterator<T> buscarColuna(Class<?> classe,
			Class<T> classeColuna, String coluna) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final String query = "select" + coluna + " from " + classe.getName();
		final List<T> lista = (List<T>) pm.newQuery(query).execute();

		pm.close();

		return new DAOIterator<T>(lista.iterator(), pm);
	}

	public static synchronized void inserirBoxConjunto(ConfiguracaoBox box) {
		CONFIGURACAO_BOXES.getBoxes().add(box);
		CONFIGURACAO_BOXES.getChavesBoxes().add(box.getKey());

		salvar(CONFIGURACAO_BOXES);
	}

	public static synchronized void excluirBoxConjunto(ConfiguracaoBox box) {
		CONFIGURACAO_BOXES.getBoxes().remove(box);
		CONFIGURACAO_BOXES.getChavesBoxes().remove(box.getKey());

		salvar(CONFIGURACAO_BOXES);
	}

	public static void salvarEstabelecimento(Estabelecimento estabelecimento) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		String urlSite = estabelecimento.getUrlSite();
		
		pm.makePersistent(estabelecimento);
		
		if(urlSite == null || urlSite.equals("")){
			urlSite = "/Site.html?eid=" + KeyFactory.keyToString(estabelecimento.getKey());
			estabelecimento.setUrlSite(urlSite);
		}
		for(Localizacao localizacao:estabelecimento.getLocalizacoes()){
			localizacao.setEstabelecimento(estabelecimento.getKey());
			pm.makePersistent(localizacao);
		}
		
		for(UsuarioLocus usuario:estabelecimento.getAdministradores()){
			usuario.getChavesEstabelecimentos().add(estabelecimento.getKey());
			usuario.getEstabelecimentos().add(estabelecimento);
			pm.makePersistent(usuario);
		}
		
		pm.close();
	}

	public static ConjuntoBoxes buscarBoxes() {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		
		final ConjuntoBoxes retorno = new ConjuntoBoxes();
		final List<Key> chavesBoxes = CONFIGURACAO_BOXES.getChavesBoxes();
		
		final Query queryBoxes = pm.newQuery(ConfiguracaoBox.class, ":p.contains(key)");
		final Query queryTweets = pm.newQuery("select from " + Tweet.class.getName()
				+ " where :p.contains(chaveConfiguracaoEstabelecimento) order by timestamp desc range 0,10");

		List<ConfiguracaoBox> configuracoes;
		Box box;
		List<Tweet> tweets;

		if (!chavesBoxes.isEmpty()) {
			configuracoes = (List<ConfiguracaoBox>) queryBoxes.execute(chavesBoxes);

			for (ConfiguracaoBox configuracao : configuracoes) {
				box = new Box();
				List<Key> chavesEstabelecimentos = configuracao.getChavesEstabelecimentos();
				if (!chavesEstabelecimentos.isEmpty()) {
					tweets = new ArrayList<Tweet>((List<Tweet>) queryTweets.execute(chavesEstabelecimentos));

					box.setTweets(new ArrayList<Tweet>(tweets));
				}

				box.setNomeBox(configuracao.getNomeBox());
				retorno.inserirBox(box);
			}
		}

		pm.close();

		return retorno;
	}

	public static List<Estabelecimento> buscarOrdenado(Class<Estabelecimento> classe, String ordenacao) {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		final Query query = pm.newQuery(Estabelecimento.class);
		final List<Estabelecimento> retorno; 
		query.setOrdering(ordenacao);
		
		retorno = new ArrayList<Estabelecimento>((List<Estabelecimento>) query.execute());
		
		pm.close();
		
		return retorno;
	}

	public static void excluirTodos() {
		final PersistenceManager pm = PMF_SINGLETON.getPersistenceManager();
		Collection dados;

		CONFIGURACAO_BOXES.getBoxes().clear();
		CONFIGURACAO_BOXES.getChavesBoxes().clear();
		pm.makePersistent(CONFIGURACAO_BOXES);

		dados = (Collection)pm.newQuery(ConfiguracaoBox.class).execute();
		if(!dados.isEmpty()){
			pm.deletePersistentAll(dados);
		}

		dados = (Collection)pm.newQuery(Tweet.class).execute();
		if(!dados.isEmpty()){
			pm.deletePersistentAll(dados);
		}

		dados = (Collection)pm.newQuery(Box.class).execute();
		if(!dados.isEmpty()){
			pm.deletePersistentAll(dados);
		}

		dados = (Collection)pm.newQuery(Estabelecimento.class).execute();
		if(!dados.isEmpty()){
			pm.deletePersistentAll(dados);
		}

		dados = (Collection)pm.newQuery(UsuarioLocus.class).execute();
		if(!dados.isEmpty()){
			pm.deletePersistentAll(dados);
		}
	}
}
